package com.ccys.netty.config;

import com.ccys.netty.content.BaseConstant;
import com.ccys.netty.handler.*;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler;
import io.netty.handler.codec.serialization.ObjectEncoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import java.net.InetAddress;


/**
 * Netty设置
 * 参考 https://blog.csdn.net/qq_38038507/article/details/132619446
 *
 * netty 使用实例
 * https://blog.csdn.net/zch981964/article/details/130631162
 * netty 官方api文档
 * https://netty.io/4.1/api/index.html
 *
 */
@Slf4j
@Configuration
@Component
public class NettyHttpServer {

    public static int port;

    @Autowired
    private WebSocketHandler webSocketHandler;

    @Autowired
    private HttpServerHandler httpServerHandler;

    @Value("${netty.port}")
    public void setPort(int port) {
        NettyHttpServer.port = port;
    }

    public void start() throws Exception {

        // 生成自签名证书
        SelfSignedCertificate ssc = new SelfSignedCertificate();
        SslContext sslContext = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
            .build();
        //创建两个线程组 boosGroup、workerGroup
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            //创建服务端辅助启动类ServerBootstrap对象
            ServerBootstrap serverBootstrap = new ServerBootstrap();

            //设置NIO线程组
            serverBootstrap.group(bossGroup, workerGroup);
            //设置服务端通道实现类型
            serverBootstrap.channel(NioServerSocketChannel.class);
            serverBootstrap.childOption(ChannelOption.SO_REUSEADDR, true);
            //serverBootstrap.localAddress(new InetSocketAddress(port));
            //设置队列大小
            serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024)
            // 两小时内没有数据的通信时,TCP会自动发送一个活动探测数据报文
            .childOption(ChannelOption.SO_KEEPALIVE, true);
            // 添加日志处理器
            serverBootstrap.handler(new LoggingHandler(LogLevel.INFO));
            serverBootstrap.childHandler(new ChannelInitializer<NioSocketChannel>() {
                @Override
                protected void initChannel(NioSocketChannel ch) throws Exception {
                    ChannelPipeline pipeline = ch.pipeline();
                    pipeline.addLast(new HttpServerCodec());
                    ch.pipeline().addLast(new ObjectEncoder());
                    // 以块的方式来写的处理器
                    ch.pipeline().addLast(new ChunkedWriteHandler());
                    pipeline.addLast(new HttpObjectAggregator(65536));
                    pipeline.addLast(new WebSocketFrameHandler());
                    ch.pipeline().addLast(new WebSocketServerProtocolHandler("/websocket", null, true, 65536 * 10));
                    // 60 秒内没有读写触发 IdleStateEvent
                    ch.pipeline().addLast(new IdleStateHandler(0 , 0 , BaseConstant.HEARTBEAT));
                    //自定义空闲状态检测(自定义心跳检测handler)
                    ch.pipeline().addLast(new HeartBeatHandler());




                    //pipeline.addLast(new WebSocketHandshakeLogger());
//                    ch.pipeline().addLast(new StringEncoder());
//                    ch.pipeline().addLast(new CustomByteToNbMessageDecoder());
                    // 添加 SslHandler 处理器
                    //pipeline.addLast(sslContext.newHandler(ch.alloc()));

                    /*ch.pipeline().addLast(
                        new HttpServerCodec(),
                        new ChunkedWriteHandler(),
                        new HttpObjectAggregator(65535),
                        //new WebSocketServerProtocolHandler("/ws"),
                        new WebSocketServerProtocolHandler("/websocket", "*", true, 65536),
                        new WebSocketFrameHandler()
                    );
                    ch.pipeline().addLast(
                        new IdleStateHandler(0, 0, 60), // 60 秒内没有读写触发 IdleStateEvent
                        new MyIdleStateHandler()
                    );*/
                    /*SslContext sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
                    ch.pipeline().addLast(sslCtx.newHandler(ch.alloc()));
                    // 添加WebSocket的编码、解码器和处理器
                    ch.pipeline().addLast(new HttpServerCodec());
                    ch.pipeline().addLast(new HttpObjectAggregator(65536));
                    ch.pipeline().addLast(new WebSocketServerProtocolHandler("/websocket"));
                    ch.pipeline().addLast(new WebSocketFrameHandler());*/


                }
            });
//            serverBootstrap.option(ChannelOption.SO_BACKLOG, 128);
//            serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);


            /*Channel channel = serverBootstrap.bind(port).sync().channel();
            log.info("=============绑定端口:{}================",port);
            channel.closeFuture().sync();*/
            //绑定端口号，同步启动服务端
            ChannelFuture channelFuture = serverBootstrap.bind(InetAddress.getLocalHost().getHostAddress(),port).sync();
            log.info("=============绑定端口:{}================", channelFuture.channel().localAddress());
            //对关闭通道进行监听
            channelFuture.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

}
